let g = null;
let lastMsg = '';
let printOutput = '';

// loader
let canvasHeight = 1024;
let canvasWidth = 1000;
let r = 40; //vertex radius
let arrow_size = 16;

// graph info
let number_nodes = 20; // 举个例子，当前代码只是创建课件里的图，并没有真正地随机创建新图
let number_edges = 60;

// 在这个函数中运行生成树算法、最短路径算法
async function run(g) {
  // 加顶点的方法：g.addVertex(new Vertex(7, 260, 170)); // 加一个顶点编号为7， x坐标为260， y坐标为170，顶点默认颜色为绿色
  // 加边的方法：g.addEdge(7, 4, 6); // 加一条边从7到4，边的权重为6，边默认颜色为黑色
  // 修改顶点颜色的方法： g.getVertex(1).color = Colors.Red
  // 修改边颜色的方法：g.getEdge(4, 2).color = Colors.Red;
  // 获取某条边的方法：let edge = g.getEdge(4, 2);
  // 获取一个顶点的方法：
  let v = g.getVertex(0);
  // 获取所有顶点的方法：
  for(let srcId of g.getVertices()) {
    g.getVertex(srcId).color = Colors.Red;
    lastMsg = `visiting node: ${srcId}`;
    await sleep(2000);
    g.getVertex(srcId).color = Colors.Green;
  }
  // 获取以一个顶点为出发点，所有边的方法：
  let neighbor = g.getNeighbors(v.id);
  for(let dstId of neighbor.keys()) {
    edge = neighbor.get(dstId);
    edge.color = Colors.Red;
    lastMsg = `dstId ${dstId}, weight: ${edge.weight}, edge color: ${edge.color}`;
    await sleep(2000);
    edge.color = Colors.Black;
  }

}

function setup() {
  // SET CANVAS AND TEXT SIZE
  const canvas = createCanvas(canvasHeight, canvasWidth);
  canvas.parent('mainContent');
  textSize(15);

  g = generateRandomGraphInCanvas(number_nodes, number_edges, canvasWidth, canvasHeight);
  run(g);
}

function draw() {
  background('white');
  drawGraph(g);
  fill('black');
  textAlign(LEFT);
  text(lastMsg, 30, 50);
  text(printOutput, 30, 70);
}

// custom helper function to deliberately slow down
// the sorting process and make visualization easy
function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

function drawGraph(g) {
  let adjList = g.getAdjList();
  for(let srcId of g.getVertices()) {
    // draw edges
    adjList.get(srcId).forEach((edge, dstId)=>{drawEdge(g.getVertex(srcId), g.getVertex(dstId), edge)});
    drawNode(g.getVertex(srcId));
  }
}

function drawNode(v) {
  ellipseMode(CENTER);
  textAlign(CENTER);
  strokeWeight(2);
  stroke(68, 114, 196);
  if (v.color == Colors.Red) {
    fill('red');
    ellipse(v.x, v.y, r, r);
  } else {
    if(v.color == Colors.Green) {
      fill(169, 209, 142);
    } else if(v.color == Colors.Grey) {
      fill(219, 219, 219);
    }
    ellipse(v.x, v.y, r, r);
  }
  fill('black');
  strokeWeight(0);
  text(str(v.id), v.x, v.y + 5);
}

function drawEdge(srcV, dstV, edge) {
  let x1 = {
    x: srcV.x,
    y: srcV.y
  };
  let x2 = {
    x: dstV.x,
    y: dstV.y
  }
  var offset = arrow_size;
  var angle = atan2(x1.y - x2.y, x1.x - x2.x); //gets the angle of the line
  stroke(0);
  strokeWeight(1);
  if(edge.color == Colors.Red) {
    strokeWeight(2);
    stroke('red');
  }
  line(x1.x, x1.y, x2.x + r * Math.cos(angle), x2.y + r * Math.sin(angle)); //draw a line beetween the vertices
  stroke('black');
  strokeWeight(0);
  text(str(edge.weight), (x1.x + x2.x) / 2, (x1.y + x2.y) /2);
  // this code is to make the arrow point
  push() //start new drawing state
  translate(x2.x + (r-offset+4) * Math.cos(angle), x2.y + (r-offset+4) * Math.sin(angle)); //translates to the destination vertex
  rotate(angle-HALF_PI); //rotates the arrow point
  if(edge.color == Colors.Red) {
    fill('red');
  }
  triangle(-offset*0.5, offset, offset*0.5, offset, 0, -offset/2); //draws the arrow point as a triangle
  fill('black');
  pop();
}